﻿#region Copyright(C) 2010-2010 NCS Pte. Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2010 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME	    : iConnect .Net Application Framework
// COMPONENT ID	    : CBEC.Core.Wcf
// COMPONENT DESC   : Core components for WCF.	
//
// CREATED DATE/BY  : 18 Aug 2010 / Gao Ye
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using CBEC.Core.Utilities;
using System;
using System.IO;
using System.IO.Compression;
using System.ServiceModel.Channels;

namespace CBEC.Core.Wcf.Compression
{
    /// <summary>
    /// The base class of all concrete compressor.
    /// </summary>
    public abstract class Compressor
    {
        /// <summary>
        /// Gets the compression stream creator.
        /// </summary>
        public abstract Func<Stream, CompressionMode, bool, Stream> CompressionStreamCreator { get; }


        /// <summary>
        /// Gets the type of the content.
        /// </summary>
        /// <value>
        /// The type of the content.
        /// </value>
        public abstract string ContentType { get; }

        /// <summary>
        /// Gets the type of the media.
        /// </summary>
        /// <value>
        /// The type of the media.
        /// </value>
        public abstract string MediaType { get; }

        /// <summary>
        /// Compresses the buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="bufferManager">The buffer manager.</param>
        /// <param name="messageOffset">The message offset.</param>
        /// <returns>
        /// The compressed <see cref="byte"/> array.
        /// </returns>
        public virtual ArraySegment<byte> Compress(ArraySegment<byte> buffer, BufferManager bufferManager, int messageOffset)
        {
            Guard.ArgumentNotNull(buffer, "buffer");
            Guard.ArgumentNotNull(bufferManager, "bufferManager");
            MemoryStream memoryStream = new MemoryStream();
            try
            {  
                using (Stream compressionStream = this.CompressionStreamCreator(memoryStream, CompressionMode.Compress, true))
                {
                    compressionStream.Write(buffer.Array, buffer.Offset, buffer.Count);
                }

                byte[] compressedBytes = memoryStream.ToArray();
                int totalLength = messageOffset + compressedBytes.Length;
                byte[] bufferedBytes = bufferManager.TakeBuffer(totalLength);

                Array.Copy(compressedBytes, 0, bufferedBytes, messageOffset, compressedBytes.Length);

                bufferManager.ReturnBuffer(buffer.Array);
                ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferedBytes, messageOffset, compressedBytes.Length);
                return byteArray;
            }
            finally 
            {
                if (memoryStream != null)
                    memoryStream.Close();
            }
        }

        /// <summary>
        /// Decompresses the buffer.
        /// </summary>
        /// <param name="buffer">The buffer.</param>
        /// <param name="bufferManager">The buffer manager.</param>
        /// <returns>
        /// The decompressed <see cref="byte"/> array.
        /// </returns>
        public virtual ArraySegment<byte> Decompress(ArraySegment<byte> buffer, BufferManager bufferManager)
        {
            Guard.ArgumentNotNull(buffer, "buffer");
            Guard.ArgumentNotNull(bufferManager, "bufferManager");

            MemoryStream memoryStream = new MemoryStream(buffer.Array, buffer.Offset, buffer.Count);
            MemoryStream decompressedStream = new MemoryStream();
            try
            {
                int totalRead = 0;
                int blockSize = 1024;
                byte[] tempBuffer = bufferManager.TakeBuffer(blockSize);
                using (Stream compressionStream = this.CompressionStreamCreator(memoryStream, CompressionMode.Decompress, false))
                {
                    while (true)
                    {
                        int bytesRead = compressionStream.Read(tempBuffer, 0, blockSize);
                        if (bytesRead == 0)
                        {
                            break;
                        }
                        decompressedStream.Write(tempBuffer, 0, bytesRead);
                        totalRead += bytesRead;
                    }
                }
                bufferManager.ReturnBuffer(tempBuffer);
                byte[] decompressedBytes = decompressedStream.ToArray();
                byte[] bufferManagerBuffer = bufferManager.TakeBuffer(decompressedBytes.Length + buffer.Offset);
                Array.Copy(buffer.Array, 0, bufferManagerBuffer, 0, buffer.Offset);
                Array.Copy(decompressedBytes, 0, bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);

                ArraySegment<byte> byteArray = new ArraySegment<byte>(bufferManagerBuffer, buffer.Offset, decompressedBytes.Length);
                bufferManager.ReturnBuffer(buffer.Array);

                return byteArray;
            }
            finally
            {
                if (memoryStream != null)
                    memoryStream.Close();
                if (decompressedStream != null)
                    decompressedStream.Close();
            }
        }
    }
}